home *** CD-ROM | disk | FTP | other *** search
/ Cream of the Crop 26 / Cream of the Crop 26.iso / program / ddj0897.zip / RCSC.ZIP / LIB86 / KERNEL.C < prev    next >
C/C++ Source or Header  |  1997-01-12  |  12KB  |  644 lines

  1. /*
  2. ** Concurrent Small C Multi-tasking Kernel
  3. ** Copyright 1996 Andy Yuen
  4. ** All rights reserved.
  5. */
  6. #include <stdio.h>
  7.  
  8. /* processor-dependent implementation: 8086 version */
  9.  
  10. #define TIMERINT    0x8
  11.  
  12. /* task descriptor structure and constants */
  13. #define TD_NEXT         0    /* points to next descriptor */
  14. #define TD_PRIOR        1    /* task priority */
  15. #define TD_SP           2    /* task's stack pointer */
  16. #define TD_DELTA    3    /* delta value for Delay */
  17.  
  18. #define TD_SIZE         (4 * sizeof(int))    /* task descriptor size */
  19.  
  20. #define REGISTERS    9    /* number of registers to save */
  21. #define STKSIZE         512    /* default task stack size */
  22. #define PRIOR           64    /* default task priority */
  23. #define FLAGS           0x200    /* initial flag register value */
  24.  
  25. /* interrupt descriptor structure and constants */
  26. #define INT_NEXT    0    /* points to next descriptor */
  27. #define INT_NUM        1    /* interrupt number */
  28. #define INT_OPCODE    2    /* inter-segment call to common ISR */
  29. #define INT_OFF        3
  30. #define INT_SEG        4
  31. #define INT_USER    5    /* address of user-defined ISR */
  32. #define INT_COND    6    /* interrupt condition variable */
  33. #define INT_ERROR    7    /* error variable */
  34.  
  35. /* offset of important info relative to INT_USER */
  36. #define OF_NUM        -4
  37. #define OF_USER        0
  38. #define OF_COND        1
  39. #define OF_ERROR    2
  40.  
  41. /* defines a NOP(0x90) followed by a inter-segment CALL(0x9A) instruction */
  42. #define OPCODE        0x9A90
  43.  
  44. #define INT_SIZE    (8 * sizeof(int))    /* interrupt descriptor size */
  45.  
  46. static int *running;    /* running task */
  47. static int *runcopy;    /* copy of running task descriptor */
  48. static int *ready;    /* ready task list */
  49. static int *delta;    /* delta list for time delay processing */
  50. static int *intr;    /* interrupt descriptor list */
  51. static int *entry;    /* temporary interrupt entry pointer */
  52.  
  53. static int timerset;    /* timer ISR set by user flag */
  54. static int mtask;    /* multiple tasks declared flag */
  55. static int reg_cs;    /* 8086's CS register value */
  56. static int reg_ds;    /* 8086's DS register value */
  57. static int critical[2]; /* DOS critical pointer */
  58.  
  59. /* insert task descriptor into specified queue according to priority.
  60.     The larger the priority value, the higher the priority. */
  61. void _Insert(queue, td)
  62. int *queue; int *td;
  63. {
  64.     int *last, *current;
  65.  
  66.     last = 0; current = *queue;
  67.     while ((current) && (current[TD_PRIOR] >= td[TD_PRIOR]))
  68.     {
  69.         last = current;
  70.         current = current[TD_NEXT];
  71.     }
  72.  
  73.     td[TD_NEXT] = current;
  74.  
  75.     if (last)
  76.         last[TD_NEXT] = td;
  77.     else
  78.         *queue = td;
  79. }
  80.  
  81. /* switch from the task whose descriptor is in runcopy to that in running.
  82.     runcopy and running have been set up by caller */
  83. void _Transfer()
  84. {
  85. #asm
  86.     ADD    SP, 2            ;remove stack frame
  87.     PUSHF                ;simulate an interrupt
  88.     PUSH    _REG_CS
  89.     MOV    AX, OFFSET _HERE    ;AX destroyed, no problem
  90.     PUSH    AX
  91.  
  92.     PUSH    AX
  93.     PUSH    BX
  94.     PUSH    CX
  95.     PUSH    DX
  96.     PUSH    SI
  97.     PUSH    DI
  98.     PUSH    BP
  99.     PUSH    DS
  100.     PUSH    ES
  101.  
  102.     MOV     BX, _RUNCOPY
  103.     MOV     4[BX], SP
  104.  
  105.     MOV     BX,_RUNNING
  106.     MOV     SP, 4[BX]
  107.  
  108.     POP    ES
  109.     POP    DS
  110.     POP    BP
  111.     POP    DI
  112.     POP    SI
  113.     POP    DX
  114.     POP    CX
  115.     POP    BX
  116.     POP    AX
  117.     IRET
  118. _HERE:
  119.     RET
  120. #endasm
  121.  
  122. }
  123.  
  124. /* move the running task to toq and dispatch first task from ready */
  125. void _Preempt(toq)
  126. int *toq;
  127. {
  128.     runcopy = running;
  129.     if (toq)
  130.         _Insert(toq, running);
  131.  
  132.     running = ready;
  133.     ready = running[TD_NEXT];
  134.     _Transfer();
  135. }
  136.  
  137. /* wait on condition: used inside a monitor */
  138. void Wait(cond)
  139. int *cond;
  140. {
  141.     runcopy = running;
  142.     _Insert(cond, running);
  143.  
  144.     running = ready;
  145.     ready = running[TD_NEXT];
  146.     _Transfer();
  147. }
  148.  
  149.  
  150. /* signal condition: used inside a monitor */
  151. void Signal(cond)
  152. int *cond;
  153. {
  154.     if (*cond)
  155.         {
  156.         runcopy = running;
  157.         _Insert(&ready, running);
  158.  
  159.         running = *cond;
  160.         *cond = running[TD_NEXT];
  161.         _Transfer();
  162.         }
  163. }
  164.  
  165.  
  166. /* put running task in ready queue and dispatch first one in ready queue */
  167. void Yield()
  168. {
  169.     disable();
  170.     if (ready)
  171.         _Preempt(&ready);
  172.     enable();
  173. }
  174.  
  175.  
  176. /* delay for the specified number of ticks */
  177. Delay(ticks)
  178. int ticks;
  179. {
  180.     int *last, *current;
  181.  
  182.     /* return 1 if timer ISR not installed */
  183.     if (!timerset)
  184.         return 1;
  185.  
  186.     if (ticks <= 0)
  187.         return 0;
  188.  
  189.     disable();
  190.  
  191.     /* insert into delta list */
  192.     last = 0; current = delta;
  193.     while ((current) && (ticks >= current[TD_DELTA]))
  194.     {
  195.         last = current;
  196.         current = current[TD_NEXT];
  197.         ticks -= last[TD_DELTA];
  198.     }
  199.  
  200.     running[TD_NEXT] = current;
  201.     running[TD_DELTA] = ticks;
  202.  
  203.     if (last)
  204.         last[TD_NEXT] = running;
  205.     else
  206.         delta = running;
  207.  
  208.     _Preempt(NULL);
  209.  
  210.     enable();
  211.  
  212.     return 0;
  213. }
  214.  
  215.  
  216. /* process delta list and move delayed tasks to ready queue if it is time */
  217. _dodelta()
  218. {
  219.     if (delta)
  220.         {
  221.         if (!--delta[TD_DELTA])
  222.             {
  223.             int *current;
  224.             int *last;
  225.  
  226.             current = delta;
  227.             while ((current) && (!current[TD_DELTA]))
  228.                 {
  229.                 last = current;
  230.                 current = current[TD_NEXT];
  231.                 _Insert(&ready, last);
  232.                 }
  233.             delta = current;
  234.             }
  235.         }
  236.  
  237.     /* return 1 to inform common ISR to do task switch */
  238.     return 1;
  239. }
  240.  
  241.  
  242. /* create a new task */
  243. void _Task(fn, priority, stacksize)
  244. int (*fn)(); int priority; int stacksize;
  245. {
  246.     int *block;
  247.     int *ptr;
  248.     int size;
  249.  
  250.     /* allocate task descriptor */
  251.     block = calloc(1, TD_SIZE);
  252.     block[TD_PRIOR] = (priority)? priority: PRIOR;
  253.  
  254.     /* allocate stack for task */
  255.     size = (stacksize)? stacksize: STKSIZE;
  256.     ptr = calloc(1, size);
  257.     ptr = block[TD_SP] = ptr + size / sizeof(int) - 
  258.         REGISTERS - 3;
  259.  
  260.     /* set up task starting address and registers in stack */
  261. #asm
  262.     MOV    _REG_DS,  DS
  263. #endasm
  264.     *ptr = ptr[1] = reg_ds;
  265.     ptr += REGISTERS;
  266. #asm
  267.     MOV    _REG_CS,  CS
  268. #endasm
  269.     *ptr++ = fn; *ptr++ = reg_cs; *ptr = FLAGS;
  270.     _Insert(&ready, block);
  271. }
  272.  
  273.  
  274. /* system idle task */
  275. void _Idle()
  276. {
  277.     while (1)
  278.     {
  279.  
  280. /* It does not matter whether Idle calls Yield() or not. User and timer
  281.     interrupts will do the job of task switching.
  282.         Yield();
  283. */
  284.     }
  285. }
  286.  
  287.  
  288. /* start multi-tasking by converting caller usually main() into a task 
  289.     and perform a task switch */
  290. void _Go()
  291. {
  292.     int *block;
  293.  
  294.     /* create idle task */
  295.     _Task(_Idle, 1, NULL);
  296.  
  297.     /* make caller into a task */
  298.     block = calloc(1, TD_SIZE);
  299.     block[TD_PRIOR] = PRIOR;
  300.  
  301.     /* start multi-tasking */
  302.     running = block;
  303.     if (!timerset && mtask)
  304.         _AddIntr(_dodelta, TIMERINT);
  305.     Yield();
  306. }
  307.  
  308. /* commom interrupt handler. Every user-handled interrupt jumps here first */
  309. void _handler()
  310. {
  311. #asm
  312.     XCHG    AX, 4[BP]    ;get segment(DS) and save AX
  313.     XCHG    BX, 2[BP]    ;get offset(USER handler) and save BX
  314.     POP    BP        ;adjust stack
  315.     PUSH    CX        ;save registers
  316.     PUSH    DX
  317.     PUSH    SI
  318.     PUSH    DI
  319.     PUSH    BP
  320.     PUSH    DS
  321.     PUSH    ES
  322.  
  323.     MOV    DS, AX
  324.  
  325.     PUSH    BX        ;save a copy for later use
  326.     CALL    [BX]        ;invoke user interrupt function
  327.  
  328.     POP    BX
  329.     MOV    4[BX], AX    ;save error code
  330.     MOV    _ENTRY, BX
  331.  
  332. #endasm
  333.     /* if user function return non-zero, it is time to wake up
  334.         task waiting on this interrupt, if one is waiting */
  335.     if (entry[OF_ERROR] && entry[OF_COND]) {
  336.         if (entry[OF_NUM] != TIMERINT) {
  337.             runcopy = entry[OF_COND];
  338.             entry[OF_COND] = runcopy[TD_NEXT];
  339.             _Insert(&ready, runcopy);
  340.             }
  341. #asm
  342.         ;don't switch if DOS is in critical section
  343.         LES    DI, DWORD PTR _CRITICAL
  344.         TEST    BYTE PTR ES:[DI], 0FFH
  345.         JNZ    _FIN
  346. #endasm
  347.         /* save task context */
  348.         if (ready) {
  349.             runcopy = running;
  350.             _Insert(&ready, running);
  351.             running = ready;
  352.             ready = running[TD_NEXT];
  353.  
  354. #asm
  355.             MOV     BX, _RUNCOPY
  356.             MOV     4[BX], SP
  357.  
  358.             MOV     BX,_RUNNING
  359.             MOV     SP, 4[BX]
  360. #endasm
  361.             }
  362.  
  363.         }
  364.  
  365.     /* perform time-of-day processing if this is a timer interrupt */
  366.     if (entry[OF_NUM] == TIMERINT) {
  367. #asm
  368.         INT    78H
  369. #endasm
  370.         }
  371. #asm
  372. _FIN:
  373.         MOV    AL, 20H
  374.         OUT    20H, AL
  375.  
  376.         POP    ES
  377.         POP    DS
  378.         POP    BP        ;restore registers
  379.         POP    DI
  380.         POP    SI
  381.         POP    DX
  382.         POP    CX
  383.         POP    BX
  384.         POP    AX
  385.         IRET
  386. #endasm
  387.  
  388. }
  389.  
  390.  
  391. /* create interrupt descriptor and set up user-defined interrupt handler */
  392. void _AddIntr(fn, num)
  393. int (*fn)(); int num;
  394. {
  395.     int *block;
  396.  
  397.     /* create interrupt descriptor */
  398.     block = calloc(1, INT_SIZE);
  399.     block[INT_NUM] = num;
  400.     block[INT_OPCODE] = OPCODE;
  401.     block[INT_OFF] = _handler;
  402.     block[INT_SEG] = reg_cs;
  403.     block[INT_USER] = fn;
  404.     _Insert(&intr, block);
  405.  
  406.     /* special timer ISR handling */
  407.     if (num == TIMERINT)
  408.         {
  409.         block[INT_COND] = ready;
  410.         timerset = 1;
  411.         /* need to save old interrupt vector for invocation 
  412.             to do time-of-day processing. interrupt
  413.             0x78 is used for storing it */
  414. #asm
  415.         PUSH    DS
  416.         PUSH    DS
  417.         MOV    AX, 3508H
  418.         INT    21H
  419.         MOV    AX, 2578H
  420.         PUSH    ES
  421.         POP    DS
  422.         MOV    DX, BX
  423.         INT    21H
  424.         POP    DS
  425.         POP    ES
  426. #endasm
  427.         }
  428.  
  429.     /* install ISR */
  430. #asm
  431.     MOV    DX, -2[BP]    ;address is &block[INT_OPCODE]
  432.     ADD    DX, 4
  433.     MOV    AX, 4[BP]    ;interrupt number
  434.     MOV    AH, 25H
  435.     INT    21H
  436. #endasm
  437. }
  438.  
  439. /* concurrent Small C initialization: monitor initialization, 
  440.     task declaration, interrupt handler setup, convert main 
  441.     into a task and start multi-tasking */
  442. void _cscinit ()
  443. {
  444.     int reges;
  445.  
  446. #asm
  447.     MOV    AX, DS
  448.     MOV    -2[BP], ES
  449.     PUSH    ES
  450.  
  451. MNTRS SEGMENT WORD PUBLIC
  452. MNTRS ENDS
  453.     ;scan monitor table for entries and call function if non-zero
  454.     MOV    AX, MNTRS
  455.     MOV    ES, AX
  456.     MOV    SI, -2
  457. _NXTMON:
  458.     ADD    SI, 2
  459.     MOV    BX, ES:[SI]
  460.     CMP    BX, 0
  461.     JZ    _NXTMON
  462.     CMP    BX, 1A1AH
  463.     JZ    _TASKTAB
  464.     PUSH    SI
  465.     PUSH    ES
  466.     MOV    ES, -2[BP]
  467.  
  468.     CALL    BX
  469.  
  470.     POP    ES
  471.     POP    SI
  472.     JMP    _NXTMON
  473.  
  474. _TASKTAB:
  475.  
  476. TASKS SEGMENT WORD PUBLIC
  477. TASKS ENDS
  478.     ;scan task table for entries and create task
  479.     MOV    AX, TASKS
  480.     MOV    ES, AX
  481.     MOV    SI, -6
  482. _NXTTASK:
  483.     ADD    SI, 6
  484. _SKIPADD:
  485.     MOV    BX, ES:[SI]
  486.     CMP    BX, 0
  487.     JNZ    _NXTCMP
  488.     ADD    SI, 2
  489.     JMP    _SKIPADD
  490. _NXTCMP:
  491.     CMP    BX, 1A1AH
  492.     JZ    _INTRTAB
  493.     PUSH    SI
  494.     PUSH    ES
  495.  
  496.     PUSH    BX
  497.     MOV    AX, ES:2[SI]
  498.     PUSH    AX
  499.     MOV    AX, ES:4[SI]
  500.     PUSH    AX
  501.     MOV    CL, 3
  502.     CALL    __TASK
  503.     ADD    SP, 6
  504.     INC    _MTASK        ;set multi-task flag
  505.  
  506.     POP    ES
  507.     POP    SI
  508.     JMP    _NXTTASK
  509. _INTRTAB:
  510.  
  511. INTRS SEGMENT WORD PUBLIC
  512. INTRS ENDS
  513.     ;scan interrupt table for interrupt service routines
  514.     MOV    AX, INTRS
  515.     MOV    ES, AX
  516.     MOV    SI, -4
  517. _NXTINTR:
  518.     ADD    SI, 4
  519.     MOV    BX, ES:[SI]
  520.     CMP    BX, 0
  521.     JZ    _NXTINTR
  522.     CMP    BX, 1A1AH
  523.     JZ    _TASKCVT
  524.     PUSH    SI
  525.     PUSH    ES
  526.  
  527.     PUSH    BX
  528.     MOV    AX, ES:2[SI]
  529.     PUSH    AX
  530.     MOV    CL, 2
  531.     CALL    __ADDINTR
  532.     ADD    SP, 4
  533.  
  534.     POP    ES
  535.     POP    SI
  536.     JMP    _NXTINTR
  537. _TASKCVT:
  538.  
  539.     ;GET DOS CRITICAL FLAG LOCATION          
  540.     MOV      AH,34H
  541.     INT      21H
  542.     MOV      _CRITICAL,BX
  543.     MOV      _CRITICAL+2,ES
  544.  
  545.     POP    ES
  546. #endasm
  547.  
  548.     _Go();
  549.  
  550. }
  551.  
  552. /* initiate i/o operation and make requesting task wait in interrupt 
  553.     condition variable. Return -1 in retcode if no such interrupt 
  554.     number in interrupt list */
  555. void StartIO(intrnum, fn, retcode)
  556. int intrnum; int (*fn)(); int *retcode;
  557. {
  558.     int *entry;
  559.     int errcode;
  560.  
  561.     disable();
  562.  
  563.     /* locate interrupt in interrupt descriptor list */
  564.     for (entry = intr; entry != NULL; entry = entry[INT_NEXT])
  565.         {
  566.         if (entry[INT_NUM] == intrnum)
  567.             break;
  568.         }
  569.  
  570.     /* initiate i/o operation and wait */
  571.     if (entry) {
  572.         if (fn) {
  573.             if ((errcode = (*fn)()) == 0) {
  574.                 Wait(&entry[INT_COND]);
  575.                 }
  576.             else {
  577.                 entry[INT_ERROR] = errcode;
  578.                 }
  579.             }
  580.         else
  581.             Wait(&entry[INT_COND]);
  582.  
  583.         *retcode = entry[INT_ERROR];
  584.         }
  585.     else
  586.         *retcode = -1;
  587.  
  588.     enable();
  589. }
  590.  
  591. /* get the priority of running task */
  592. GetPrior()
  593. {
  594.     return running[TD_PRIOR];
  595. }
  596.  
  597. /* set the priority of running task */
  598. void SetPrior(prior)
  599. int prior;
  600. {
  601.     running[TD_PRIOR] = prior;
  602. }
  603.  
  604. /* get a byte from input port */
  605. inpbyte(port)
  606. int port;
  607. {
  608. #asm
  609.     MOV    DX, 4[BP]
  610.     IN    AL, DX
  611.     XOR    AH, AH
  612. #endasm
  613. }
  614.  
  615. /* write a byte to output port */
  616. void outpbyte(port, value)
  617. int port; int value;
  618. {
  619. #asm
  620.     MOV    DX, 6[BP]
  621.     MOV    AX, 4[BP]
  622.     OUT    DX, AL
  623. #endasm
  624. }
  625.  
  626. /* disable interrupt */
  627. void disable()
  628. {
  629. #asm
  630.     CLI
  631. #endasm
  632. }
  633.  
  634.  
  635. /* enable interrupt */
  636. void enable()
  637. {
  638. #asm
  639.     STI
  640. #endasm
  641. }
  642.  
  643.  
  644.